/**
 * This fragment shader module contains functions to perform
 * all necessary visualization techniques with the texuregrammar
 * shader (texgramshader.frag)
 */

// first colour is white, for no edge.
vec4 COLOURS_[21] = vec4[21]( vec4(0.0, 0.0, 0.0, 0.0),

		 vec4(1.0, 0.0, 0.0, 1.0),
		 vec4(0.95, 0.05, 0.0, 1.0),
		 vec4(0.90, 0.10, 0.0, 1.0),
		 vec4(0.85, 0.15, 0.0, 1.0),
		 vec4(0.80, 0.20, 0.0, 1.0),
		 vec4(0.75, 0.25, 0.0, 1.0),
		 vec4(0.70, 0.30, 0.0, 0.0),
		 vec4(0.65, 0.35, 0.0, 1.0),
		 vec4(0.60, 0.40, 0.0, 1.0),
		 vec4(0.55, 0.45, 0.0, 1.0),
		 vec4(0.50, 0.50, 0.0, 1.0),
		 vec4(0.45, 0.55, 0.0, 1.0),
		 vec4(0.40, 0.60, 0.0, 1.0),
		 vec4(0.35, 0.65, 0.0, 1.0),
		 vec4(0.30, 0.70, 0.0, 1.0),
		 vec4(0.25, 0.75, 0.0, 1.0),
		 vec4(0.20, 0.80, 0.0, 1.0),
		 vec4(0.15, 0.85, 0.0, 1.0),
		 vec4(0.10, 0.90, 0.0, 1.0),
		 vec4(0.05, 0.95, 0.0, 1.0) );

/*****************
 * LIC algorithm *
 *****************/
vec4 LIC(sampler2D tx, sampler2D flow, vec2 coord, vec2 vpdim, int kernel){

	vec2 tc = coord;
	vec4 res = texture(tx, clamp(tc/vpdim, 0.0, 1.0));

	vec2 tempv;
	int counter = 1;
	int k = kernel;
	// This piece of code assumes vector length as importance measure by using it as a scaling factor
	// for the kernel size. TODO: design a scalar field in C++ that records the importance of the
	// vector field at each point - either discrete (1's and 0's) or continuous on [0,1]
	vec2 vc = (texture(flow, clamp(tc/vpdim, 0.0, 1.0)).xy * 2) - vec2(1,1);
	float ln = sqrt(vc.x*vc.x + vc.y*vc.y);
	k = int( ceil(kernel * ln) );

	for(int i=0; i<k; ++i){
		tc += (texture(flow, clamp(tc/vpdim, 0.0, 1.0)).xy * 2) - vec2(1,1);
		if( tc.x < 0 || tc.x > vpdim.x
	 	 || tc.y < 0 || tc.y > vpdim.y ){ break; }
		res += texture(tx, clamp(tc/vpdim, 0.0, 1.0));
		++counter;
	}
	tc = coord;
	for(int i=0; i<k; ++i){
		tc -= (texture(flow, clamp(tc/vpdim, 0.0, 1.0)).xy * 2) - vec2(1,1);
		if( tc.x < 0 || tc.x > vpdim.x
	 	 || tc.y < 0 || tc.y > vpdim.y ){ break; }
		res += texture(tx, clamp(tc/vpdim, 0.0, 1.0));
		++counter;
	}
	res /= counter;
	return res;
}


/********************************************************
 * Isoline algorithm - very crude one, max 20 isovalues *
 ********************************************************/
vec4 isoline(sampler2D tx, vec2 coord, vec2 vpdim, float[20] isovalues){
	float c = texture(tx, clamp(coord/vpdim, 0.0, 1.0)).x;

	int col = 0;
	for(int i=0, l = isovalues.length; i<l; ++i){
		if(isovalues[i] == 0.0){ break; }
		float sn = c - isovalues[i];
// if the sign is different it'll be a negative number and thus an isoline
		if( sn * (texture(tx, clamp((coord-vec2(1,0))/vpdim, 0.0, 1.0)).x - isovalues[i]) < 0.0
		 || sn * (texture(tx, clamp((coord-vec2(-1,0))/vpdim, 0.0, 1.0)).x - isovalues[i]) < 0.0
		 || sn * (texture(tx, clamp((coord-vec2(0,1))/vpdim, 0.0, 1.0)).x - isovalues[i]) < 0.0
		 || sn * (texture(tx, clamp((coord-vec2(0,-1))/vpdim, 0.0, 1.0)).x - isovalues[i]) < 0.0 )
		{
			col = i+1;
			break;
		}
	}
	return COLOURS_[col];
}

/*************************************************************************************************************
 * Arrowplot and streamlines - these are pre-rendered in the cpp processor, so just return the sampled value *
 *************************************************************************************************************/
vec4 arrowPlot(sampler2D img, vec2 coord, vec2 vpdim){
	return texture(img, clamp(coord/vpdim, 0.0, 1.0));
}
vec4 streamlines(sampler2D img, vec2 coord, vec2 vpdim){
	return texture(img, clamp(coord/vpdim, 0.0, 1.0));
}

/*************
 * Colormaps *
 *************/
vec4 heatmap(const float m) {
    const int colorTableSize = 4;
    vec3 colorTable[colorTableSize];
    colorTable[0] = vec3(0.0, 0.0, 0.0);    // black
    colorTable[1] = vec3(1.0, 0.0, 0.0);    // red
    colorTable[2] = vec3(1.0, 1.0, 0.0);    // yellow
    colorTable[3] = vec3(1.0, 1.0, 1.0);    // white

    float numColors = float(colorTableSize - 1);
    float v = clamp(m * numColors, 0.0, numColors);
    ivec2 limits = clamp(ivec2(int(v), int(ceil(v))), 0, colorTableSize);
    vec3 color = mix(colorTable[limits.x], colorTable[limits.y], fract(v));
    return vec4(color, 1.0);
}
vec4 redToBlueMap(const float m) {
	const int colorTableSize = 11;
	vec3 colorTable[colorTableSize];
	colorTable[0] = vec3(0.192157, 0.211765, 0.584314);
	colorTable[1] = vec3(0.270588, 0.458824, 0.705882);
	colorTable[2] = vec3(0.454902, 0.678431, 0.819608);
	colorTable[3] = vec3(0.670588, 0.85098, 0.913725);
	colorTable[4] = vec3(0.878431, 0.952941, 0.972549);
	colorTable[5] = vec3(1.0, 1.0, 0.74902);
	colorTable[6] = vec3(0.996078, 0.878431, 0.564706);
	colorTable[7] = vec3(0.992157, 0.682353, 0.380392);
	colorTable[8] = vec3(0.956863, 0.427451, 0.262745);
	colorTable[9] = vec3(0.843137, 0.188235, 0.152941);
	colorTable[10] = vec3(0.647059, 0.0, 0.14902);

    float numColors = float(colorTableSize - 1);
    float v = clamp(m * numColors, 0.0, numColors);
    ivec2 limits = clamp(ivec2(int(v), int(ceil(v))), 0, colorTableSize);
    vec3 color = mix(colorTable[limits.x], colorTable[limits.y], fract(v));
    return vec4(color, 1.0);
}
vec4 isoluminantGreenMap(const float m) {
	const int colorTableSize = 3;
	vec3 colorTable[colorTableSize];
    colorTable[0] = vec3(0.8980392, 0.9607843, 0.8784314);
    colorTable[1] = vec3(0.6313725, 0.8509804, 0.6078431);
    colorTable[2] = vec3(0.1921569, 0.6392157, 0.3294118);

    float numColors = float(colorTableSize - 1);
    float v = clamp(m * numColors, 0.0, numColors);
    ivec2 limits = clamp(ivec2(int(v), int(ceil(v))), 0, colorTableSize);
    vec3 color = mix(colorTable[limits.x], colorTable[limits.y], fract(v));
    return vec4(color, 1.0);
}









